package de.rs.espresso.algorithms.minimumcover.model;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Matrix<T> implements Cloneable {

	private List<Column<T>> columns;
	private List<Row> rows;

	public Matrix(List<BitSet> rows, List<T> columns) {
		this.columns = Column.toColumnList(columns);
		this.rows = Row.toRowList(rows);
	}

	private Matrix(List<Row> rows, List<Column<T>> columns, boolean clone) {
		this.columns = columns;
		this.rows = rows;
	}

	public List<Column<T>> getColumns() {
		return columns;
	}

	public int countRows() {
		// TODO Auto-generated method stub
		return rows.size();
	}

	public List<Row> getRows() {
		return rows;
	}

	public void removeColumnDominance() {
		List<Row> rotatedRows = Row.rotateRows(rows, columns.size());

		Collections.sort(rotatedRows, new Comparator<Row>() {

			@Override
			public int compare(Row row1, Row row2) {

				return row2.cardinality() - row1.cardinality();
			}

		});

		int rowCount = 0;
		List<Integer> removableColumns = new ArrayList<Integer>();
		for (Row row : rotatedRows) {
			rowCount++;
			if (!removableColumns.contains(row.getPosition())) {
				for (int i = rowCount; i < rotatedRows.size(); i++)
					if (!removableColumns.contains(rotatedRows.get(i).getPosition())
							&& row.covers(rotatedRows.get(i))) {
						removableColumns.add(rotatedRows.get(i).getPosition());
					}
			}

		}

		List<Row> newRows = new ArrayList<Row>();
		for (Row r : rows) {
			newRows.add(Row.removeColumns(r, removableColumns, columns.size()));
		}

		ArrayList<Column<T>> toRemove = new ArrayList<Column<T>>();
		for (Column<T> c : columns) {
			if (removableColumns.contains(c.getPosition()))
				toRemove.add(c);
		}

		rows = newRows;
		columns.removeAll(toRemove);

	}

	public void removeRowDominance() {
		List<Row> toRemove = new ArrayList<Row>();

		Collections.sort(rows, new Comparator<Row>() {

			@Override
			public int compare(Row row1, Row row2) {
				return row2.cardinality() - row1.cardinality();
			}

		});

		int rowCount = 0;
		for (Row row : rows) {
			rowCount++;
			for (int i = rowCount; i < rows.size(); i++)
				if (row.covers(rows.get(i)))
					toRemove.add(row);
		}

		rows.removeAll(toRemove);

	}

	public List<Column<T>> popEssentials() {

		List<Column<T>> result = new ArrayList<Column<T>>();
		List<Integer> essentialColumnNumbers = new ArrayList<Integer>();
		List<Row> toRemove = new ArrayList<Row>();

		for (Row r : rows) {
			if (r.cardinality() == 1) {
				int columnNumber = r.getEssentialColumnNumber();
				if (!essentialColumnNumbers.contains(columnNumber))
					essentialColumnNumbers.add(columnNumber);
				toRemove.add(r);
			}

		}
		rows.removeAll(toRemove);

		int j = 0;

		for (Column<T> c : columns) {
			if (essentialColumnNumbers.contains(j))
				result.add(c);

			j++;
		}

		return result;
	}

	public Column<T> selectColumnToBranch(List<Row> independentSet) {
		double maxColumnWeight = 0;
		Column<T> maxColumn = null;
		for (Column<T> c : columns) {
			double columnWeight = 0;
			for (Row r : rows) {
				columnWeight = r.getWeight(c.getPosition());
			}
			if (columnWeight >= maxColumnWeight) {
				for (Row r : independentSet) {
					if (r.getElement().get(c.getPosition())) {
						maxColumnWeight = columnWeight;
						maxColumn = c;
						break;
					}
				}
			}
		}

		return maxColumn;
	}

	public Matrix<T> clone() {

		List<Row> clonedRows = new ArrayList<Row>();

		for (Row r : rows)
			clonedRows.add(r.clone());

		return new Matrix<T>(clonedRows, columns, true);

	}

	public void remove(Column<T> column) {
		List<Row> newRows = new ArrayList<Row>();
		for (Row r : rows) {
			newRows.add(Row.removeColumn(r, column.getPosition()));
		}
	}

	@Override
	public String toString() {
		String result = "";

		for (Column<T> column : columns) {
			result += "\t" + column.getPosition();
		}

		result += "\n---";

		for (@SuppressWarnings("unused")
		Column<T> column : columns) {
			result += "\t---";
		}

		for (Row row : rows) {
			result += "\n" + row.getPosition() + "|";
			for (int i = 0; i < columns.size(); i++) {
				result += "\t" + (row.getElement().get(i) ? "1" : "0");
			}
		}

		return result + "\n";

	}

}
